#pragma once
#include <iostream>
#include <string>
#include "RoadNode.h"
#include "Constants.h"
#include <vector>
#include <cmath>
#include <iomanip>
#include <fstream>
#include "PLF.h"
using namespace std;

struct RoadGraph {
	vector<RoadNode*> nodes; 
	int node_num, total_time;
	float delta;
	int countPar = 0;
	int count_edge = 0;

	RoadGraph(float delta) {
		fstream fin(tgraph_path);
		int nodeNum, totalTime;
		fin >> nodeNum >> totalTime;
		node_num = nodeNum;
		total_time = totalTime;

		this->delta = delta;
		createNode(node_num, road_coor);

		clock_t tbegin = clock(), tend;
		readGraph(fin);
		tend = clock();
		cout << "road_graph time :  " << to_string(tend - tbegin) << "ms" << endl << endl;
	}

	void createNode(int nodeNum) {
		for (int i = 0; i < nodeNum; i++) {
			RoadNode* tempNode = new RoadNode(i);
			nodes.push_back(tempNode);
		}
	}

	void createNode(int nodeNum, string road_coor) {
		fstream fin(road_coor);
		int nodeId;
		float x, y;
		while (fin >> nodeId >> x >> y) {
			RoadNode* tempNode = new RoadNode(nodeId, x, y);
			nodes.push_back(tempNode);
		}
	}

	void readGraph(fstream& fin) {
		int vs, vt, weight_piece_num;
		while (fin >> vs >> vt >> weight_piece_num) {
			float t, w;
			float temp_avg = 0;
			float min_value = INT_MAX, max_value = INT_MIN;
			PLF ini_plf;
			auto f = std::make_shared<std::vector<Segment>>(weight_piece_num); 
			if (weight_piece_num == 1) {
				fin >> t >> w;
				temp_avg = w;
				(*f)[0] = { t , w , INTV_CNTED };
				min_value = max_value = w; 
			}
			else {
				int pre_t, pre_w;
				for (int i = 0; i < weight_piece_num; i++) { 
					fin >> t >> w;
					(*f)[i] = { t, w , INTV_CNTED };
					min_value = min(min_value, w);
					max_value = max(max_value, w);
					if (i != 0) {
						temp_avg += countAvgTravelTime(pre_w, w, t - pre_t, total_time);
					}
					pre_t = t;
					pre_w = w;
				}
				if (pre_t < 86400) temp_avg += countAvgTravelTime(pre_w, pre_w, 86400 - pre_t, total_time);
			}
			ini_plf.f = f;
			if (max_value >= delta) {
				cut(vs, vt, delta, temp_avg, max_value, min_value, ini_plf, weight_piece_num);
				countPar++;
			}
			else {
				nodes[vs]->addPLF(vt, f);
				nodes[vs]->addMin_Max(vt, Min_Max_Value{ min_value , max_value });
				nodes[vs]->addEdge(vt, countNewWeight(1, 0.01, temp_avg)); 
				count_edge++;
			}
		}
	}

	float countAvgTravelTime(int start, int end, int time_diff, int total_time) {
		float area = (start + end) * time_diff / 2.0;
		return area / total_time;
	}

	float countNewWeight(int c, float a, float num) {
		return c * exp(-1 * a * num);
	}

	int cnt = 0;
	void cut(int vs, int vt, float delta, float avg, float max_v, float min_v, PLF ini_plf, int segment_num) {
		int m = ceil(max_v / delta);
		vector <PLF> plf_f;
		vector <PLF> plf_F;
		plf_F.push_back(ini_plf);
		plf_F[0].max_value = max_v;
		plf_F[0].min_value = min_v;

		int next_node;

		for (int i = 0; i < m; i++) {
			float min_value_F = INT_MAX, max_value_F = INT_MIN, min_value_f = INT_MAX, max_value_f = INT_MIN;
			if (i == 0) {
				next_node = node_num + cnt;
				RoadNode* tempNode = new RoadNode(next_node);
				nodes.push_back(tempNode);
				auto new_f = std::make_shared<std::vector<Segment>>(segment_num);
				for (int j = 0; j < segment_num; j++) {
					(*new_f)[j] = { (*ini_plf.f)[j].t, (*ini_plf.f)[j].w * delta / plf_F[i].max_value, INTV_CNTED };
					min_value_f = min(min_value_f, (*ini_plf.f)[j].w * delta / plf_F[i].max_value);
					max_value_f = max(max_value_f, (*ini_plf.f)[j].w * delta / plf_F[i].max_value);
				}

				PLF tem_plf_f;
				tem_plf_f.f = new_f;
				tem_plf_f.max_value = max_value_f;
				tem_plf_f.min_value = min_value_f;
				plf_f.push_back(tem_plf_f);
				addAll(vs, next_node, tem_plf_f, max_value_f, min_value_f);
				cnt++;
			}
			else if (i == m - 1) {
				auto new_G = std::make_shared<std::vector<Segment>>(segment_num);
				if (segment_num == 1) {
					float bf = (*plf_f[i - 1].f)[0].w;
					float bF = (*plf_F[i - 1].f)[0].w;
					float bg = bF - bf;
					float wg = bg;
					float tg = (*plf_f[i - 1].f)[0].w + (*plf_f[i - 1].f)[0].t;
					if (tg >= 86400.0) {
						(*new_G)[0] = { 86400.0 , wg , INTV_CNTED };
					}
					else {
						(*new_G)[0] = { tg , wg , INTV_CNTED };
					}
					min_value_F = min(min_value_F, wg);
					max_value_F = max(max_value_F, wg);
				}
				else {
					for (int j = 0; j < segment_num - 1; j++) {
						float delta_y_f = (*plf_f[i - 1].f)[j].w - (*plf_f[i - 1].f)[j + 1].w;
						float delta_x_f = (*plf_f[i - 1].f)[j].t - (*plf_f[i - 1].f)[j + 1].t;
						float kf = delta_y_f / delta_x_f;

						float delta_y_F = (*plf_F[i - 1].f)[j].w - (*plf_F[i - 1].f)[j + 1].w;
						float delta_x_F = (*plf_F[i - 1].f)[j].t - (*plf_F[i - 1].f)[j + 1].t;
						float kF = delta_y_F / delta_x_F;

						float bf = (*plf_f[i - 1].f)[j].w - kf * (*plf_f[i - 1].f)[j].t;
						float bF = (*plf_F[i - 1].f)[j].w - kF * (*plf_F[i - 1].f)[j].t;

						float kg = (kF - kf) / (kf + 1);
						float bg = bF - bf * (kF + 1) / (kf + 1);
						if (j == 0) {
							float wg = kg * ((*plf_f[i - 1].f)[j].t + (*plf_f[i - 1].f)[j].w) + bg;
							float tg = (*plf_f[i - 1].f)[j].t + (*plf_f[i - 1].f)[j].w;
							if (tg > 86400.0) {
								(*new_G)[j] = { 86400.0 , wg , INTV_CNTED };
								break;
							}
							else {
								(*new_G)[j] = { tg, wg , INTV_CNTED };
							}
							min_value_F = min(min_value_F, wg);
							max_value_F = max(max_value_F, wg);
							float wg1 = kg * ((*plf_f[i - 1].f)[j + 1].t + (*plf_f[i - 1].f)[j + 1].w) + bg;
							float tg1 = (*plf_f[i - 1].f)[j + 1].t + (*plf_f[i - 1].f)[j + 1].w;
							if (tg1 > 86400.0) {
								(*new_G)[j + 1] = { 86400.0, wg1 , INTV_CNTED };
								break;
							}
							else {
								(*new_G)[j + 1] = { tg1, wg1 , INTV_CNTED };
							}
							min_value_F = min(min_value_F, wg1);
							max_value_F = max(max_value_F, wg1);
						}
						else {
							float wg = kg * ((*plf_f[i - 1].f)[j + 1].t + (*plf_f[i - 1].f)[j + 1].w) + bg;
							float tg = (*plf_f[i - 1].f)[j + 1].t + (*plf_f[i - 1].f)[j + 1].w;
							if (tg > 86400.0) {
								(*new_G)[j + 1] = { 86400.0 , wg , INTV_CNTED };
								break;
							}
							else {
								(*new_G)[j + 1] = { tg , wg , INTV_CNTED };
							}
							min_value_F = min(min_value_F, wg);
							max_value_F = max(max_value_F, wg);
						}
					}
				}
				PLF tem_plf_f, tem_plf_F;
				tem_plf_f.f = new_G;
				tem_plf_f.max_value = max_value_F;
				tem_plf_f.min_value = min_value_F;
				plf_f.push_back(tem_plf_f);
				addAll(next_node, vt, tem_plf_f, max_value_F, min_value_F);
			}
			else {
				next_node = node_num + cnt;
				RoadNode* tempNode = new RoadNode(next_node); 
				nodes.push_back(tempNode);
				auto new_G = std::make_shared<std::vector<Segment>>(segment_num);
				auto new_f = std::make_shared<std::vector<Segment>>(segment_num);

				if (segment_num == 1) {
					float bf = (*plf_f[i - 1].f)[0].w;
					float bF = (*plf_F[i - 1].f)[0].w;
					float bg = bF - bf;
					float wg = bg;
					float tg = (*plf_f[i - 1].f)[0].w + (*plf_f[i - 1].f)[0].t;
					if (tg >= 86400.0) {
						(*new_G)[0] = { 86400.0 , wg , INTV_CNTED };
					}
					else {
						(*new_G)[0] = { tg , wg , INTV_CNTED };
					}
					min_value_F = min(min_value_F, wg);
					max_value_F = max(max_value_F, wg);
				}
				else {
					for (int j = 0; j < segment_num - 1; j++) {
						float delta_y_f = (*plf_f[i - 1].f)[j].w - (*plf_f[i - 1].f)[j + 1].w;
						float delta_x_f = (*plf_f[i - 1].f)[j].t - (*plf_f[i - 1].f)[j + 1].t;
						float kf = delta_y_f / delta_x_f;

						float delta_y_F = (*plf_F[i - 1].f)[j].w - (*plf_F[i - 1].f)[j + 1].w;
						float delta_x_F = (*plf_F[i - 1].f)[j].t - (*plf_F[i - 1].f)[j + 1].t;
						float kF = delta_y_F / delta_x_F;

						float bf = (*plf_f[i - 1].f)[j].w - kf * (*plf_f[i - 1].f)[j].t;
						float bF = (*plf_F[i - 1].f)[j].w - kF * (*plf_F[i - 1].f)[j].t;

						float kg = (kF - kf) / (kf + 1);
						float bg = bF - bf * (kF + 1) / (kf + 1);
						if (j == 0) {
							float wg = kg * ((*plf_f[i - 1].f)[j].t + (*plf_f[i - 1].f)[j].w) + bg;
							float tg = (*plf_f[i - 1].f)[j].t + (*plf_f[i - 1].f)[j].w;
							if (tg > 86400.0) {
								(*new_G)[j] = { 86400.0 , wg , INTV_CNTED };
								break;
							}
							else {
								(*new_G)[j] = { tg, wg , INTV_CNTED };
							}
							min_value_F = min(min_value_F, wg);
							max_value_F = max(max_value_F, wg);

							float wg1 = kg * ((*plf_f[i - 1].f)[j + 1].t + (*plf_f[i - 1].f)[j + 1].w) + bg;
							float tg1 = (*plf_f[i - 1].f)[j + 1].t + (*plf_f[i - 1].f)[j + 1].w;
							if (tg1 > 86400.0) {
								(*new_G)[j + 1] = { 86400.0, wg1 , INTV_CNTED };
								break;
							}
							else {
								(*new_G)[j + 1] = { tg1, wg1 , INTV_CNTED };
							}
							min_value_F = min(min_value_F, wg1);
							max_value_F = max(max_value_F, wg1);
						}
						else {
							float wg = kg * ((*plf_f[i - 1].f)[j + 1].t + (*plf_f[i - 1].f)[j + 1].w) + bg;
							float tg = (*plf_f[i - 1].f)[j + 1].t + (*plf_f[i - 1].f)[j + 1].w;
							if (tg > 86400.0) {
								(*new_G)[j + 1] = { 86400.0 , wg , INTV_CNTED };
								break;
							}
							else {
								(*new_G)[j + 1] = { tg , wg , INTV_CNTED };
							}
							min_value_F = min(min_value_F, wg);
							max_value_F = max(max_value_F, wg);
						}
					}

				}
				PLF tem_plf_F;
				tem_plf_F.f = new_G;
				plf_F.push_back(tem_plf_F);
				plf_F[i].max_value = max_value_F;
				plf_F[i].min_value = min_value_F;
				if (segment_num == 1) {
					float tf = (*plf_f[i - 1].f)[0].w + (*plf_f[i - 1].f)[0].t;
					if (tf > 86400.0) {
						(*new_f)[0] = { 86400.0, (*plf_F[i].f)[0].w * delta / plf_F[i].max_value , INTV_CNTED };
					}
					else {
						(*new_f)[0] = { tf , (*plf_F[i].f)[0].w * delta / plf_F[i].max_value , INTV_CNTED };
					}
					min_value_f = min(min_value_f, (*plf_F[i].f)[0].w * delta / plf_F[i].max_value);
					max_value_f = max(max_value_f, (*plf_F[i].f)[0].w * delta / plf_F[i].max_value);

				}
				else {
					for (int j = 0; j < segment_num - 1; j++) {
						if (j == 0) {
							float tf = (*plf_f[i - 1].f)[j].t + (*plf_f[i - 1].f)[j].w;
							if (tf > 86400.0) {
								(*new_f)[j] = { 86400.0 , (*plf_F[i].f)[j].w * delta / plf_F[i].max_value , INTV_CNTED };
								break;
							}
							else {
								(*new_f)[j] = { tf , (*plf_F[i].f)[j].w * delta / plf_F[i].max_value , INTV_CNTED };
							}
							min_value_f = min(min_value_f, (*plf_F[i].f)[j].w * delta / plf_F[i].max_value);
							max_value_f = max(max_value_f, (*plf_F[i].f)[j].w * delta / plf_F[i].max_value);

							float tf1 = (*plf_f[i - 1].f)[j + 1].t + (*plf_f[i - 1].f)[j + 1].w;
							if (tf1 > 86400.0) {
								(*new_f)[j + 1] = { 86400.0 ,  (*plf_F[i].f)[j + 1].w * delta / plf_F[i].max_value , INTV_CNTED };
								break;
							}
							else {
								(*new_f)[j + 1] = { tf1,  (*plf_F[i].f)[j + 1].w * delta / plf_F[i].max_value , INTV_CNTED };
							}
							min_value_f = min(min_value_f, (*plf_F[i].f)[j + 1].w * delta / plf_F[i].max_value);
							max_value_f = max(max_value_f, (*plf_F[i].f)[j + 1].w * delta / plf_F[i].max_value);
						}
						else {
							float tf = (*plf_f[i - 1].f)[j + 1].t + (*plf_f[i - 1].f)[j + 1].w;
							if (tf > 86400.0) {
								(*new_f)[j + 1] = { 86400.0, (*plf_F[i].f)[j + 1].w * delta / plf_F[i].max_value , INTV_CNTED };
								break;
							}
							else {
								(*new_f)[j + 1] = { tf , (*plf_F[i].f)[j + 1].w * delta / plf_F[i].max_value , INTV_CNTED };
							}
							min_value_f = min(min_value_f, (*plf_F[i].f)[j + 1].w * delta / plf_F[i].max_value);
							max_value_f = max(max_value_f, (*plf_F[i].f)[j + 1].w * delta / plf_F[i].max_value);

						}
					}

				}

				PLF tem_plf_f;
				tem_plf_f.f = new_f;
				tem_plf_f.max_value = max_value_f;
				tem_plf_f.min_value = min_value_f;
				plf_f.push_back(tem_plf_f);
				addAll(next_node - 1, next_node, tem_plf_f, max_value_f, min_value_f);
				cnt++;
			}
		}
	}

	void addAll(int n1, int n2, PLF plf, float max_v, float min_v) {
		plf.max_value = max_v;
		plf.min_value = min_v;
		nodes[n1]->plf[n2] = plf;
		nodes[n1]->addMin_Max(n2, Min_Max_Value{ plf.min_value , plf.max_value });
		nodes[n1]->addEdge(n2);
		count_edge++;
	}
};
